package model;

import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;

import Controller.EventManager;
import Controller.GameController;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.media.MediaPlayer.OnSeekCompleteListener;
import android.view.SurfaceView;

public class Movie implements OnBufferingUpdateListener, OnPreparedListener,
		OnSeekCompleteListener, OnCompletionListener {
	private int id;
	// private List<MovieEvent> events;
	public Map<Integer, MovieEvent> events;
	public LinkedList<MovieEvent> sortedEvents;
	public UrlFile url;
	public Boolean isOnline;
	public Result[] endOfMovieResult;
	public SurfaceView mView;
	public boolean isPrepeared;
	public boolean isReleased;
	public MediaPlayer mp;
	public boolean dontRelease;
	public EventManager eventManager;
	public GameController controller;
	int timerSeekTime;
	public boolean playAfterSeek;
	public String localPath;
	public LinkedList<Bitmap> frames;
	private boolean isSorted = false;

	
	public Movie(int id, Map<Integer, MovieEvent> events, UrlFile url,
			Boolean isOnline, Result[] endOfMovieResult) {
		mp = new MediaPlayer();
		isReleased = false;
		this.id = id;
		isPrepeared = false;
		dontRelease = false;
		this.events = events;
		this.url = url;
		this.isOnline = isOnline;
		this.endOfMovieResult = endOfMovieResult;
		frames = new LinkedList<Bitmap>();
	}

	public Movie(int _id, String _url, boolean _isOnline) {
		id = _id;
		url = new UrlFile(_url);
		isOnline = _isOnline;
		mp = new MediaPlayer();
		isReleased = false;
		isPrepeared = false;
		dontRelease = false;
		frames = new LinkedList<Bitmap>();
	}

	public SurfaceView getMView() {
		return mView;
	}

	public void setMView(SurfaceView view) {
		mView = view;
	}


	public int getId() {
		return id;
	}

	public Result[] getEndOfMovieResult() {
		return endOfMovieResult;
	}

	public void setEndOfMovieResult(Result[] endOfMovieResult) {
		this.endOfMovieResult = endOfMovieResult;
	}

	public Boolean getIsOnline() {
		return isOnline;
	}

	public void setIsOnline(Boolean isOnline) {
		this.isOnline = isOnline;
	}

	public Collection<MovieEvent> getEvents() {
		return events.values();
	}

	public MovieEvent getEvent(int eventId) {
		if (events.containsKey(eventId))
			return events.get(eventId);

		return null;
	}

	public void setEvents(Map<Integer, MovieEvent> events) {
		this.events = events;
	}

	public UrlFile getUrl() {
		return url;
	}

	public void setUrl(UrlFile url) {
		this.url = url;
	}

	public boolean isPrepeared() {
		return isPrepeared;
	}

	public void setPrepeared(boolean isPrepeared) {
		this.isPrepeared = isPrepeared;
	}

	public void setId(int id) {
		this.id = id;
	}

	public void prepare(SurfaceView view) throws Exception {
		mView = view;
		prepare();
	}
	
	public void stop()
	{
		mp.stop();
	}

	public void prepare() throws Exception {
		if (isPrepeared) {
			mp.stop();
		} else {
			if (isReleased) {
				isReleased = false;
				mp = new MediaPlayer();
			}
			if (isOnline &&  (localPath == null || localPath.equals("")))//online == don't use cache...
				mp.setDataSource(url.getUrl());
			else
				mp.setDataSource(localPath);
		}
		mp.setOnBufferingUpdateListener(this);
		mp.setOnPreparedListener(this);
		mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
		mp.setDisplay(mView.getHolder());
		mp.setScreenOnWhilePlaying(true);
		mp.setOnSeekCompleteListener(this);
		mp.prepare();
		isPrepeared = true;
		return;
	}

	public boolean play() throws Exception {
		twoTimesHack = false;
		mp.start();
		return true;
	}

	public void release() {
		if (isReleased || dontRelease)
			return;

		isReleased = true;
		isPrepeared = false;
		if (mp.isPlaying()) {
			mp.stop();
		}
		if (mp != null)
		{
			mp.release();
			mp = null;
		}
		for (Iterator<Bitmap> iterator = frames.iterator(); iterator.hasNext();) {
			Bitmap frame = iterator.next();		
			frame.recycle();
		}
		
		System.gc();
	}

	public void forceRelease() {
		if (mp.isPlaying()) {
			mp.stop();
		}
		if (mp != null)
			mp.release();
	}

	@Override
	public void onBufferingUpdate(MediaPlayer mp, int percent) {
	}

	@Override
	public void onPrepared(MediaPlayer mp) {
	}

	@Override
	public void onSeekComplete(MediaPlayer mp){
		if (!playAfterSeek)
			return;
		
		try {
			play();
			if (controller != null)
				controller.playingCallback(this, timerSeekTime);
		} catch (Exception e) {
			controller.raiseErrorMessage(e.getMessage());
		}
	}

	public void setOnCompletionListener(EventManager eventManager) {
		this.eventManager = eventManager;
		mp.setOnCompletionListener(this);
	}

	public void pause() throws Exception {
		mp.pause();
	}

	public void seekTo(int i) throws Exception {
		playAfterSeek = true;
		timerSeekTime = i;
		mp.seekTo(i);
	}
	
	public void seekTo(int i, GameController controller) throws Exception {
		playAfterSeek = true;
		timerSeekTime = i;
		this.controller = controller;
		mp.seekTo(i);
	}
	
	public void seekToAndDontPlay(int i)
	{
		playAfterSeek = false;
		mp.seekTo(i);
	}

	public void start() throws Exception {
		mp.start();
	}

	@Override
	public String toString() {
		return url.toString();
	}

	private boolean twoTimesHack = false;
	@Override
	public void onCompletion(MediaPlayer mp) {
		if (!twoTimesHack)//First time
		{
			twoTimesHack = true;
			eventManager.onCompletion(this.mp);
		}
		else//Second time
		{
			twoTimesHack = false;
		}
	}
	
	public LinkedList<MovieEvent> getSortedEvents()
	{
		if (isSorted)
			return sortedEvents;
		isSorted = true;
		if (sortedEvents == null)
			sortedEvents = new LinkedList<MovieEvent>();
		for (MovieEvent event : getEvents()) {
			sortedEvents.add(event);
		}
		Collections.sort(sortedEvents);
		return sortedEvents;
	}
}
